package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.wordmemorize.common.aspect.operate.OperateLog;
import com.cskaoyan.wordmemorize.common.aspect.operate.OperateLogConstant;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.converter.LevelConverter;
import com.cskaoyan.wordmemorize.dao.entity.LevelDO;
import com.cskaoyan.wordmemorize.dao.mapper.LevelMapper;
import com.cskaoyan.wordmemorize.dto.admin.DeleteLevelOperateDTO;
import com.cskaoyan.wordmemorize.dto.admin.LevelDTO;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.request.BatchDeleteCommand;
import com.cskaoyan.wordmemorize.request.LevelCommand;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.service.LevelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class LevelServiceImpl implements LevelService {

    @Autowired
    LevelMapper levelMapper;

    @Autowired
    LevelConverter levelConverter;

    // todo:等级表中存储了等级解锁的价格，该价格应与商品表中的等级价格一致，因此前端页面中并未指定等级价格，
    //  而是应该由后端在保存等级的时候，查询商品表的到等级解锁价格并保存到leve表;
    //  ;
    //  如果想实现这种效果，前端在添加/修改等级时不应该提供等级价格的选项；不过为了体现这种关系，
    //  可以设置以下条件：等级的等级值value应该唯一，即可以通过value找到唯一等级，
    //  此外，在添加/修改登记时，会按照填入的等级值value去商品表中查找对应的等级解锁商品，获得价格，
    //  要求只有添加/修改登记时填入的价格与商品中价格一致时才能添加/修改

    @Override
    @OperateLog(dateType = "等级", operateType = OperateLogConstant.OPERATE_ADD)
    public void addLevel(LevelCommand command) {
        // 注意，观察前端页面，发现除了描述属性之外，其他全都不为空，故不用判空。
        // 此外，等级值、签到天数、解锁价格都一定>=0，故不用判断这些属性是否为负.且签到类型也是只能二选一，故签到类型也不用判断；
        // 但是，数据库中没有设置等级名称唯一，严谨起见，为了保证等级名称的唯一，这里先查是否重复，不重复才能插入

        // 在Service层中要使用DO对象，因为DO继承BaseDO的id和is_deleted，否则无法实现逻辑删除
        LevelDO levelDO = levelConverter.levelCommandToLevelDO(command);
        LambdaQueryWrapper<LevelDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LevelDO::getLevelName, levelDO.getLevelName())
                .or()
                .eq(LevelDO::getLevelValue, levelDO.getLevelValue());
        List<LevelDO> list = levelMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(list)){         // 没有重复的，可插入
            int insert = levelMapper.insert(levelDO);
            if (insert == 0){
                throw new BusinessException("插入level表失败", 500);
            }
        }else {
            throw new BusinessException("level表中存在同名的等级/相同的等级值", 500);
        }
    }

    @Override
    @OperateLog(dateType = "等级", operateType = OperateLogConstant.OPERATE_UPDATE)
    public void updateLevel(LevelCommand command) {
        LevelDO levelDO = levelConverter.levelCommandToLevelDO(command);
        // 请求体JSON中有id属性，根据id来更新就可以了;
        // 还是为了保证等级名称与等级值的唯一，这里先查是否重复，不重复才能插入
        LevelDO originLevel = levelMapper.selectById(levelDO.getId());

        LambdaQueryWrapper<LevelDO> levelNameQueryWrapper = new LambdaQueryWrapper<>();
        levelNameQueryWrapper.eq(LevelDO::getLevelName, levelDO.getLevelName());
        LambdaQueryWrapper<LevelDO> levelValueQueryWrapper = new LambdaQueryWrapper<>();
        levelValueQueryWrapper.eq(LevelDO::getLevelValue, levelDO.getLevelValue());
        List<LevelDO> levelNamelist = levelMapper.selectList(levelNameQueryWrapper);
        List<LevelDO> levelValuelist = levelMapper.selectList(levelValueQueryWrapper);

        LambdaQueryWrapper<LevelDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(LevelDO::getLevelName, levelDO.getLevelName())
                .or()
                .eq(LevelDO::getLevelValue, levelDO.getLevelValue());
        List<LevelDO> list = levelMapper.selectList(queryWrapper);

        if ((originLevel.getLevelName().equals(levelDO.getLevelName()) && originLevel.getLevelValue().equals(levelDO.getLevelValue()))
                || (originLevel.getLevelName().equals(levelDO.getLevelName()) && CollectionUtils.isEmpty(levelValuelist))
                || (originLevel.getLevelValue().equals(levelDO.getLevelValue()) && CollectionUtils.isEmpty(levelNamelist))
                || CollectionUtils.isEmpty(list)){
            int updated = levelMapper.updateById(levelDO);
            if (updated == 0){
                throw new BusinessException("更新等级失败", 500);
            }
        }else {
            throw new BusinessException("level表中存在同名的等级/相同的等级值，不能修改", 500);
        }
    }

    @Override
    public PageDTO<LevelDTO> getLevelList(PageRequest request) {
        if(request.getPageNum()== null) {
            //查询所有等级列表
            List<LevelDO> levelDOs = levelMapper.selectList(null);
            return levelConverter.levelDOToPageDTO(levelDOs,levelDOs.size());
        }
        //需要分页
        Page<LevelDO> page = new Page<>(request.getPageNum(), request.getCount());
        LambdaQueryWrapper<LevelDO> queryWrapper = new LambdaQueryWrapper<>();
        // keyword不为null，说明是模糊搜索；如果为null，不进入if，无处理，则为全表查询
        if (request.getKeyword() != null){
            queryWrapper.like(LevelDO::getLevelName, request.getKeyword());     // like方法是 %AA%
        }
        // Page<LevelDO>代表 封装LevelDO的页，可以获取该页的所有LevelDO
        //  LevelDO虽然有levelPrivileges属性，按理来说应该查level_privilege表，
        //  获得这个等级所拥有的所有特权，但是在服务器的后台中，这个属性为null，故这里不查level_privilege表
        Page<LevelDO> resultPage = levelMapper.selectPage(page, queryWrapper);
        List<LevelDO> levelDOS = resultPage.getRecords();

        // 获得符合查询条件的总数，而不是一页中的个数；如果没keyword请求参数，则为全表查询，有keyword，则模糊查询，已经在上面定义好了
        List<LevelDO> list = levelMapper.selectList(queryWrapper);
        PageDTO<LevelDTO> levelDTOPageDTO = levelConverter.levelDOToPageDTO(levelDOS, list.size());
        return levelDTOPageDTO;
    }

    @Override
    @OperateLog(dateType = "等级", operateType = OperateLogConstant.OPERATE_SINGLE_DELETE)
    public DeleteLevelOperateDTO deleteLevel(Long id) {
        LevelDO levelDO = levelMapper.selectById(id);
        int i = levelMapper.deleteById(id);
        if (i == 0){
            throw new BusinessException("删除id为"+id+"的等级失败！", 500);
        }

        DeleteLevelOperateDTO deleteLevelOperateDTO = new DeleteLevelOperateDTO();
        deleteLevelOperateDTO.setId(id);
        deleteLevelOperateDTO.setLevelName(levelDO.getLevelName());
        return deleteLevelOperateDTO;
    }

    // 开启事务，只有所有id对应的等级都存在，能能被删除，才提交事务，否则回滚
    @Transactional
    @Override
    @OperateLog(dateType = "等级", operateType = OperateLogConstant.OPERATE_BATCH_DELETE)
    public List<DeleteLevelOperateDTO> deleteLevels(BatchDeleteCommand command) {
        List<String> ids = command.getIds();
        List<Long> idList = ids.stream().map(Long::parseLong).collect(Collectors.toList());
        List<LevelDO> levelDOS = levelMapper.selectByIds(idList);
        int i = levelMapper.deleteByIds(idList);
        if (i != ids.size()){
            throw new BusinessException("有id对应的等级不存在", 500);
        }

        List<DeleteLevelOperateDTO> deleteLevelOperateDTOS = levelConverter.levelDOsToDeleteLevelOperateDTOs(levelDOS);
        return deleteLevelOperateDTOS;
    }
}
